/*
 *  uiApplication.cpp
 *  trail-map-editor
 *
 *  Created by Giorgio Tino on 3/4/13.
 *  Copyright 2013 __MyCompanyName__. All rights reserved.
 *
 */

#include "ui.h"

#include <stdio.h>
#include <stdlib.h>

#include "ui.h"

#include "tinyxml2/tinyxml2.h"

#define UI_APPLICATION_DEFAULT_FULLSCREEN false
#define UI_APPLICATION_DEFAULT_CENTERED false
#define UI_APPLICATION_DEFAULT_POSITION_X 0
#define UI_APPLICATION_DEFAULT_POSITION_Y 0
#define UI_APPLICATION_DEFAULT_WIDTH 1024
#define UI_APPLICATION_DEFAULT_HEIGHT 768
#define UI_APPLICATION_DEFAULT_NAME "UI Application"

using namespace tinyxml2;

uiApplication* uiApplication::instance = 0;

//
// GLUT callbacks
//
static void glutReshape(int w, int h)
{
	uiApplication::getInstance().reshape(w,h);
}

static void glutDisplay(void)
{	
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    glEnable(GL_DEPTH_TEST);
    
    uiApplication::getInstance().display();
    
    glutSwapBuffers();
    glutPostRedisplay () ;
}

static void glutKeyboardDown(unsigned char key, int x, int y)
{
	uiApplication::getInstance().keyDown(key,x,y);
	glutPostRedisplay();
}

static void glutKeyboardUp(unsigned char key, int x, int y)
{
	uiApplication::getInstance().keyUp(key,x,y);
	glutPostRedisplay () ;
}

static void glutSpecialDown(int key, int x, int y)
{
	uiApplication::getInstance().specialDown(key,x,y);
	glutPostRedisplay();
}

static void glutSpecialUp(int key, int x, int y)
{
	uiApplication::getInstance().specialUp(key,x,y);
	glutPostRedisplay();
}

// called when mouse is moving and no mouse button is pressed
static void glutMotion(int newx, int newy)
{
	uiApplication::getInstance().passive(newx,newy);
	glutPostRedisplay();
}

// called when mouse is moving and buttons are pressed
static void glutPassiveMotion(int newx, int newy)
{
	uiApplication::getInstance().motion(newx,newy);
	glutPostRedisplay();
}

static void glutMouse(int button, int state, int mouseX, int mouseY)
{
	uiApplication::getInstance().mouse(button,state,mouseX,mouseY);
	glutPostRedisplay();
}

static void glutIdle(void)
{
	uiApplication::getInstance().idle();
	glutPostRedisplay();
}

void uiApplication::_error_cb(enum ulSeverity severity, char* msg )
{
    if(severity == UL_DEBUG) Log.write(Debug, msg);
    else if(severity == UL_WARNING) Log.write(Warning, msg);
    else if(severity == UL_FATAL) Log.write(Error, msg);
    else if(severity == UL_MAX_SEVERITY) Log.write(Fatal, msg);
    else Log.write(PlainText, msg);
}

//
// uiApplication
//
uiApplication::~uiApplication()
{
	uiExit();
    
    Log.write(PlainText, "Application exit. Bye bye!");
}

void uiApplication::init(int argc, char *argv[], const char* configFile)
{
    Log.write(PlainText, "Initializing Application");
    
    ulSetErrorCallback(_error_cb);
    
	glutInit(&argc,argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
	
	if(configFile)
	{
		loadConfig(configFile);
	}
	else
	{
		loadDefaultConfig();
	}
    
	if(!this->fullscreen)
	{
		glutInitWindowSize (this->width, this->height);
		glutInitWindowPosition (this->x,this->y);
	}

    glutCreateWindow(this->name);
	
	//
	// Register GLUT callbacks
	//
    glutReshapeFunc(glutReshape);
    glutDisplayFunc(glutDisplay);
    glutKeyboardFunc(glutKeyboardDown);
	glutKeyboardUpFunc(glutKeyboardUp);
    glutSpecialFunc(glutSpecialDown);
    glutSpecialUpFunc(glutSpecialUp);
    glutMotionFunc(glutMotion);
    glutPassiveMotionFunc(glutPassiveMotion);
    glutMouseFunc(glutMouse);
    glutIdleFunc(glutIdle);
	
    glutSetKeyRepeat  ( GLUT_KEY_REPEAT_OFF );
	
	if(this->fullscreen)
	{
		glutFullScreen();
	}
	
    jsInit();         
	
    ssgInit();
	
//    ssgSetFOV(60.0,40.0);
//    ssgSetNearFar(0.0,30.0);
//    
//    glClearColor(0.2,0.0,0.7,1.0);
//    glShadeModel(GL_SMOOTH);
//    glEnable(GL_DEPTH_TEST);
	
    uiInit();
	
	// After uiInit()!
	setupExtensions();
    
    Log.write(PlainText, "Application Initialized");
}

void uiApplication::loadConfig(const char* configFile)
{
	if(!configFile)
	{
		loadDefaultConfig();
		ulSetError(UL_FATAL, "NULL config file specified. Loading default config...",configFile);
		return;
	}
    
    Log.write(PlainText, "Load configuration from file : %s", configFile);
	
	XMLDocument doc;
	XMLError error = doc.LoadFile(configFile);
	if(error)
	{
		loadDefaultConfig();
		ulSetError(UL_FATAL, "Config file not found : %s. Loading default config...",configFile);
		return;
	}
	
	//
	// Look for the 'config' node.
	//
	const XMLElement* configElement = doc.FirstChildElement( "config" );
	
	if(!configElement)
	{
		loadDefaultConfig();
		ulSetError(UL_FATAL, "Cannot find config element in %s. Loading default config...",configFile);
		return;
	}
	
	//
	// Load settings
	//
	const XMLElement* settingsElement = configElement->FirstChildElement(0);
	
	while(settingsElement)
	{
		if(ulStrEqual(settingsElement->Name(), "window"))
		{
			this->fullscreen = settingsElement->Attribute("fullscreen") ? settingsElement->BoolAttribute("fullscreen") : UI_APPLICATION_DEFAULT_FULLSCREEN;
			this->width = settingsElement->FloatAttribute("width");
			this->height = settingsElement->FloatAttribute("height");
			this->x = settingsElement->FloatAttribute("x");
			this->y = settingsElement->FloatAttribute("y");
			this->centered = settingsElement->Attribute("centered") ? settingsElement->BoolAttribute("centered") : UI_APPLICATION_DEFAULT_CENTERED;
			this->name = ulStrDup(settingsElement->Attribute("name"));
		}
		
		settingsElement = settingsElement->NextSiblingElement(0);
	}
}

void uiApplication::loadDefaultConfig()
{
    Log.write(PlainText, "Load default configuration");
    
	this->fullscreen = UI_APPLICATION_DEFAULT_FULLSCREEN;
	this->width = UI_APPLICATION_DEFAULT_WIDTH;
	this->height = UI_APPLICATION_DEFAULT_HEIGHT;
	this->x = UI_APPLICATION_DEFAULT_POSITION_X;
	this->y = UI_APPLICATION_DEFAULT_POSITION_Y;
	this->centered = UI_APPLICATION_DEFAULT_CENTERED;
	this->name = UI_APPLICATION_DEFAULT_NAME;
}

void uiApplication::loop()
{
	glutMainLoop();
}

void uiApplication::shutdown()
{
	//
    // NOTE : gotta free the memory and cleanup your shit!
    //
}

void uiApplication::setupExtensions()
{	
	if( uiCheckGLExtension( "GL_ARB_multitexture" ) )
	{		
		glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &numTextureUnits);
		if(numTextureUnits > 1)
			canMultitexture= true;
		else
			canMultitexture= false;
	}
	else
	{
		canMultitexture= false;	
	}
	
//	glActiveTextureARB		= ( PFNGLACTIVETEXTUREARBPROC )	     wglGetProcAddress( "glActiveTextureARB" );
//	glClientActiveTextureARB= ( PFNGLCLIENTACTIVETEXTUREARBPROC )wglGetProcAddress( "glClientActiveTextureARB" );
//	glMultiTexCoord2fARB	= ( PFNGLMULTITEXCOORD2FARBPROC )	 wglGetProcAddress( "glMultiTexCoord2fARB" );
//	glMultiTexCoord2dARB	= ( PFNGLMULTITEXCOORD2DARBPROC )	 wglGetProcAddress( "glMultiTexCoord2dARB" );
	
	Log.write( Renderer, "Multitexturing has been initiated, and your video card supports %d texture units", numTextureUnits );
	
	if( uiCheckGLExtension( "GL_EXT_compiled_vertex_array" ) )
		canCVA= true;
	else
		canCVA= false;
	
	uiCheckGLExtension( "GL_CLAMP_TO_EDGE" );
		
	uiCheckGLExtension( "GL_EXT_fog_coord" );
	
	//configure the volumetric fog system
//	if( checkExtension( "GL_EXT_fog_coord" ) )
//		glFogCoordfEXT= ( PFNGLFOGCOORDFEXTPROC )wglGetProcAddress( "glFogCoordfEXT" );
//	
//	glLockArraysEXT  = ( PFNGLLOCKARRAYSEXTPROC )  wglGetProcAddress( "glLockArraysEXT" );
//	glUnlockArraysEXT= ( PFNGLUNLOCKARRAYSEXTPROC )wglGetProcAddress( "glUnlockArraysEXT" );	
}